use axum_app_common::{error, model::SessionContext};
use axum_app_dao::{
    entity::rbac_user_role_rel, rbac_role_resource_rel_dal, rbac_user_role_rel_dal,
};
use sea_orm::{DatabaseConnection, EntityTrait};

pub struct RbacSvc<'a> {
    db_conn: &'a DatabaseConnection,
    ctx: Option<&'a SessionContext>,
}

impl<'a> RbacSvc<'a> {
    pub fn new(db_conn: &'a DatabaseConnection, ctx: Option<&'a SessionContext>) -> Self {
        Self { db_conn, ctx }
    }

    /**
     * 绑定用户角色
     */
    pub async fn bind_user_roles(&self, user_id: &str, role_codes: &[&str]) -> anyhow::Result<()> {
        let db = self.db_conn;
        for r_code in role_codes {
            let res = rbac_user_role_rel_dal::insert(db, user_id, r_code).await;
            if res.is_err() && !error::is_duplicate_exception(&res) {
                anyhow::bail!(res.err().unwrap());
            }
        }

        Ok(())
    }

    /**
     * 绑定角色资源
     */
    pub async fn bind_role_resources(
        &self,
        role_code: &str,
        res_type: &str,
        res_codes: &[&str],
    ) -> anyhow::Result<()> {
        let db = self.db_conn;
        for r_code in res_codes {
            let res = rbac_role_resource_rel_dal::insert(db, role_code, r_code, res_type).await;
            if res.is_err() && !error::is_duplicate_exception(&res) {
                anyhow::bail!(res.err().unwrap());
            }
        }

        Ok(())
    }

    pub async fn query_user_roles(
        &self,
        user_id: &str,
    ) -> anyhow::Result<Vec<rbac_user_role_rel::Model>> {
        let db = self.db_conn;
        rbac_user_role_rel_dal::RbacUserRoleRelQuery::query_by_user_id(db, user_id).await
    }
}

#[cfg(test)]
mod tests {
    use axum_app_common::test::get_connect;

    use super::*;

    #[tokio::test]
    async fn test_bind_user_roles() {
        let db_conn = get_connect().await;
        let svc = RbacSvc::new(&db_conn, None);
        let res = svc
            .bind_user_roles("81eff6810b33d042b90b27c0574f5d1ee8a1", &["ADMIN"])
            .await;
        assert!(res.is_ok());
    }

    #[tokio::test]
    async fn bind_role_resources() {
        let db_conn = get_connect().await;
        let svc = RbacSvc::new(&db_conn, None);
        let res = svc
            .bind_role_resources("ADMIN", "service", &["user.query"])
            .await;
        assert!(res.is_ok());
    }
}
